// 本文件对MIN包格式中的TLV做统一的分配
// @Author: zhengqi Wu
// @Description:
// @Version: 1.0.0
// @Date: 2021/2/24 9:11 下午
// @Copyright: MIN-Group；国家重大科技基础设施——未来网络北大实验室；深圳市信息论与未来网络重点实验室
//

////////////////////////////////////////////////////////////////////////////////////////
////// 一级：网络分组级别TLV号分配（0 ~ 49）
////////////////////////////////////////////////////////////////////////////////////////
const PacketType = {
    TlvInvalid             :0, // 无效包
    TlvPacketMINCommon     :5, // 通用MIN包
    TlvPacketMINManagement :6, // MIN管理包
    TlvPacketMINIP         :7, // MIN支持IP包
}


////////////////////////////////////////////////////////////////////////////////////////
////// 二级：网络包分区TLV号分配 （50 ~ 99）
////////////////////////////////////////////////////////////////////////////////////////
const FieldType = {
    TlvIdentifierField       :50, // 目的标识区
    TlvSignatureField        :51, // 数字签名区
    TlvReadOnlyField         :52, // 只读数据区
    TlvMutableField          :53, // 可变数据区
    TlvMutableProtectField   :54, // 可变数据区中的保护区
    TlvMutableDangerousField :55, // 可变数据区中的非受保护区
}

////////////////////////////////////////////////////////////////////////////////////////
////// 组件 （100 -> 500）
////////////////////////////////////////////////////////////////////////////////////////

const ComponentType = {
    // 标识分配
    TlvIdentifierComponent       :100, // 标识名称组件
    TlvIdentifier                :101,// 标识
    TlvIdentifierCommon          :102, // 身份标识
    TlvIdentifierContentInterest :103, // 内容兴趣标识
    TlvIdentifierContentData     :104, // 内容数据标识

    // 数字签名
    TlvSignatureValue :200, // 签名值
    TlvSignatureInfo  :201, // 签名参数
    TlvSignatureType  :202, // 签名类型
    TlvKeyLocator     :203, // 指示证书或公钥的位置

    TlvPayload          :204, // 网络包负载
    TlvCanBePrefix      :205, // 是否可以被较长的数据包前缀匹配
    TlvMustBeRefresh    :206, // 是否强制只拉取新鲜的数据
    TlvInterestLifeTime :207, // 兴趣包生存期
    TlvNonce            :208, // 随机数
    TlvHopLimit         :209, // 跳数限制
    TlvFreshnessPeriod  :210, // 数据包新鲜时间
    TlvCongestionMark   :211, // 拥塞标记
    TlvTTL              :212, // Time to live
    TlvNackHeader       :213, // Nack header
    TlvNoCache          :214, // No Cache
    TlvIncomingLogicFaceId: 215, // 入口逻辑接口地址

    // 管理通信协议
    TlvManagementControlParameters :220, // 控制命令参数
    TlvManagementLogicFaceId       : 221, // 逻辑接口Id
    TlvManagementUri               : 222 ,// 逻辑接口地址，例如: tcp://192.168.1.1:13899，存在localUri时，也可表示remoteUri

    // 管理通信协议 -> LogicFaceManager
    TlvManagementCost                 : 223, // 链路开销
    TlvManagementLogicFacePersistency : 224, // 逻辑接口持久性
    TlvManagementUriScheme            : 225, // 逻辑接口地址采用的模式，例如：tcp
    TlvManagementMtu                  : 226, // 最大传输单元
    TlvManagementCapacity             : 227, // CS容量
	TlvManagementCount                : 228, // 删除的CS条目的数量
	TlvManagementExpireTime           : 229, // 超时时间
	TlvManagementLocalUri             : 230, // 逻辑接口地址，例如: tcp://192.168.1.1:13899，存在localUri时，也可表示remoteUri
	TlvManagementPrefix               : 240, // 前缀
    TlvManagementPasswd               : 241, // 密码
    TlvManagementCommonString         : 242, // 通用字符串参数

    // LpPacket
    TlvLpPacket            : 250, // LpPacket
    TlvLpPacketHeader      : 251, // LpPacket Header
    TlvLpPacketFragmentId  : 252, // LpPacket 分片id
    TlvLpPacketFragmentNum : 253, // LpPacket 分片数
    TlvLpPacketFragmentSeq : 254, // LpPacket 分片号
}

//
// 目前已分配的最大的Tlv值，第一轮分配的最大值为500，超过该值则断定为不合法
// TODO: 目前TLV值的分配，第一轮分配都在500以内，如果以后分配更高值时，需要修改此处的限制
//
// @Description:
//
const MaxTlvNum = 500


/**
 * 从字节数组中读出一个变长整型 注意BigInt 与 Number运算
 * @param buffer ArrayBuffer
 * @param start Number
 * @returns {BigInt|Number}
 */
function readVarNumber(buffer, start) {
    const view = new DataView(buffer)
    let buf = Buffer.from(buffer)
    let length = view.byteLength
    if (start >= length) {
        // DONE 错误处理
        throw new TLVError("Insufficient data during TLV parsing")
    }
    let firstByte = buf[start]
    if (firstByte < 241){
        return (firstByte)
    }else if (firstByte === 241){
        // DONE 错误处理
        if(length - start < 3){
            throw new TLVError("Insufficient data during TLV parsing")
        }
        return (buf.readUInt16BE(start+1))
    }else if (firstByte === 242){
        // DONE 错误处理
        if(length - start < 5){
            throw new TLVError("Insufficient data during TLV parsing")
        }
        return (buf.readUInt32BE(start+1))
    }else if (firstByte === 243){
        // DONE 错误处理
        if(length - start < 9){
            throw new TLVError("Insufficient data during TLV parsing")
        }
        return (buf.readBigUInt64BE(start+1))
    }
}

/**
 * 从字节数组的指定位置中读取一个变长的整型作为TLV Type
 * @param buffer ArrayBuffer
 * @param start Number
 * @returns Number
 */
function readType(buffer, start){
    try{
        let tlvType = readVarNumber(buffer, start)
        if (tlvType === 0 || tlvType > MaxTlvNum){
            // DONE 错误处理
            throw new TLVError(`Illegal TLV-TYPE ${tlvType}`)
        }
        return tlvType
    }catch (e){
        throw e
    }
}

/**
 * 测量一个非负整数再 TLV 编码时实际占用多少字节
 * @param nonNegativeInteger Number|BigInt
 * @returns {number}
 */
function sizeOfNonNegativeInteger(nonNegativeInteger){
    switch (true){
        case nonNegativeInteger > 4294967295n:
            return 8
        case nonNegativeInteger > 65535:
            return 4
        case nonNegativeInteger > 255:
            return 2
        default:
            return 1
    }
}

/**
 * 测量一个Vlint在 TLV 编码时实际占用多少字节
 * @param varNumber Number|BigInt
 * @returns {number}
 */
function sizeOfVarNumber(varNumber){
    switch (true){
        case varNumber > 4294967295n:
            return 9
        case varNumber > 65535:
            return 5
        case varNumber >= 241:
            return 3
        default:
            return 1
    }
}
/**
 * 从字节数组的指定位置中读取一个非负类型
 * @param buffer
 * @param start Number
 * @param end Number
 * @returns {bigint|number} 32位及以下返回number类型，反之返回bigint类型
 */
function readNonNegativeInteger(buffer, start, end){
    if(start === end){
        // DONE 错误处理
        throw new TLVError('Empty buffer during TLV parsing')
    }
    if(start < 0 || end > buffer.byteLength){
        // DONE 错误处理
        throw new TLVError('Insufficient data during TLV parsing')
    }
    let size = end - start
    let view = Buffer.from(buffer)
    switch (size){
        case 1:
            return view[start]
        case 2:
            return view.readUInt16BE(start)
        case 4:
            return view.readUInt32BE(start)
        case 8:
            return view.readBigUInt64BE(start)
    }
    // DONE 错误处理
    throw new TLVError(`Invalid length for ${size} NonNegativeInteger`)
}

/**
 * @description 编码一个非负整数到一个数组当中
 * @param value Number|BigInt
 * @returns {ArrayBuffer}
 */
function buildNonNegativeIntegerArr(value){
    let buf = ''
    let view = ''
    switch (true){
        case value > 4294967295n:
            buf = new ArrayBuffer(8)
            view = Buffer.from(buf)
            view.writeBigUInt64BE(value, 0)
            return buf
        case value > 65535:
            buf = new ArrayBuffer(4)
            view = Buffer.from(buf)
            view.writeUInt32BE(value, 0)
            return buf
        default:
            buf = new ArrayBuffer(2)
            view = Buffer.from(buf)
            view.writeUInt16BE(value, 0)
            return buf
    }
}
/**
 * 检查某个type值是否是指定的TLV-type
 * @param current Number
 * @param target Number
 */
function expectType(current, target){
    if (current !== target){
        // DONE 错误处理
        throw new TLVError(`TLV type not match, current value is ${current},expect ${target}`)
    }
    return true // fix bug
}

/**
 * 根据TLV分配规则，判断某个tlvType是否是合规的标识类型
 * @param tlvType Number
 * @returns {boolean|boolean}
 */
function isValidIdentifierType(tlvType){
    return tlvType >= 102 && tlvType <= 199
}

/**
 * 根据TLV分配规则，判断某个tlvType是否是合规的包类型
 * @param tlvType Number
 * @returns {boolean|boolean}
 */
function isValidPacketType(tlvType){
    return tlvType > 0 && tlvType < 50
}


class TLVError extends Error{
    constructor(m){
        super(m)
        this.name = 'TLVError'
    }

    error(){
        console.log(`{this.name}: {this.message}`)
    }
}

module.exports = {
    PacketType: PacketType,
    FieldType: FieldType,
    ComponentType: ComponentType,
    MaxTlvNum: MaxTlvNum,
    // Vlint: Vlint,
    readVarNumber: readVarNumber,
    readType: readType,
    readNonNegativeInteger: readNonNegativeInteger,
    expectType: expectType,
    isValidIdentifierType: isValidIdentifierType,
    isValidPacketType: isValidPacketType,
    sizeOfNonNegativeInteger: sizeOfNonNegativeInteger,
    sizeOfVarNumber: sizeOfVarNumber,
    buildNonNegativeIntegerArr: buildNonNegativeIntegerArr
}